package com.example.order.service.impl;


import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.homework.Show;
import com.example.order.bean.Order;
import com.example.order.exception.MyWebException;
import com.example.order.feign.ProductFeignClient;
import com.example.order.service.OrderService;
import com.example.product.bean.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;


import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

@Service
public class OrderServiceimpl implements OrderService {

    //远程调用核心，获取微服务的信息
    @Autowired
    DiscoveryClient discoveryClient;
    //远程调用核心，发送远程调用请求
    @Autowired
    RestTemplate restTemplate;
    //负载均衡核心
    @Autowired
    LoadBalancerClient loadBalancerClient;

    //写好的feign接口，用于发出请求（远程调用）

    @Autowired
    ProductFeignClient productFeignClient;


    /**
     * 作业
     * @return
     */
    @Override
    public Show show(Long id) {
        Show show = productFeignClient.showProduct(id);
        //在源字符串的基础上在拼接上新的字符串
        show.setServiceName(show.getServiceName()+"，远程调用成功，我是消费者");
        return show;
    }



    //将这个非Web资源的方法注册为资源，使sentienl可以控制，所谓非web资源，就是指不是通过http请求来访问的资源，比如数据库、文件、第三方接口等，更简单点说就是没有使用MVC注解的那些和本次请求有关的代码
    @SentinelResource(value = "creatOrderByWjj",blockHandler = "feiwebziyuanBlockHandleer")

    //创建订单，调用下面的方法远程调用其他微服务的接口
    @Override
    public Order createOrder(Long userId, Long ProductId ) {
        //调用全参构造,调用远程调用方法，获取商品信息
        Product product = RemoteGetProduct3(ProductId);
        //创建订单，订单有多个商品，所以商品信息是一个list，数据来源是另一个微服务
        Order order = new Order(1L, new BigDecimal(10000), userId, "" , "" , Arrays.asList(product));
        return order;
    }

    //远程调用测试，调用根据id查询商品信息的接口，返回商品信息
    public Product RemoteGetProduct(Long productId){
        //1获取商品微服务所在的所有服务器的ip和端口号，先获取所有名为service-product的微服务对象
//        List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
        //2获取第一个服务器的ip和端口号
//        ServiceInstance serviceInstance = instances.get(0);

        /**  优化：
         *  负载均衡的去调用service-product微服务，获取商品微服务的实例对象
         */
        ServiceInstance serviceInstance = loadBalancerClient.choose("service-product");


        String serviceId = serviceInstance.getServiceId();  //服务id，即微服务名
        String host = serviceInstance.getHost();  //服务所在服务器的ip
        int port = serviceInstance.getPort();  //服务所在服务器的端口号
        //3拼接url，向该url发送请求，productId为要查询的商品id,是动态传入的
        String url = "http://"+host+":"+port+"/product/"+productId;
        //4发送请求，获取商品信息,RestTemplate是spring提供的一个工具类，用于发送远程调用请求
        //5发送请求，获取商品信息,Product.class是返回的商品信息的类型，url是请求路径
        Product product = restTemplate.getForObject(url, Product.class);
        return product;


    }

    //远程调用再优化：
    private Product RemoteGetProduct2(Long productId){

        //直接拼接url
        String url = "https://service-product/product/"+productId;
        //发送请求，获取商品信息,Product.class是返回的商品信息的类型，url是请求路径,使用过注解，一昵称每个请求都是负载均衡的
        Product product = restTemplate.getForObject(url, Product.class);
        return product;

    }




    //openfeign的远程调用，请求由feign包下的一个接口发出，只需要嗲用接口的方法即可
    private Product RemoteGetProduct3(Long productId){
        //直接调用接口中的  getProductById方法，传入商品id，即可获取商品信息
        Product product = productFeignClient.getProductById(productId);
        return product;
    }


    //针对非web资源的异常处理方法，引入参数BlockException，专门返回违反了什么规则而导致异常
    //也就是上面的creatOrderByWjj方法，当上面的那个方法被失效时（直接结束，需要一个返回值，参数类型完全一致的方法来代替它运行），会调用这个方法，返回一个订单对象（兜底数据）
    public Order feiwebziyuanBlockHandleer(Long userId, Long ProductId,BlockException e){
        System.out.println("请求被限流了,原因：" + e.getMessage());
        //创建一个订单对象，商品信息为空
        Order order = new Order(1L, new BigDecimal(10000), 1L,"异常", "发生异常，非web资源被限流", null);
        return order;

    }

}
